package container.queue;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;

/**
 * https://blog.csdn.net/f641385712/article/details/81145345
 * 基于链表的阻塞队列，同 ArrayListBlockingQueue 类似，其内部也维持着一个数据缓冲队列（该队列由一个链表构成）。
 * 当生产者往队列中放入一个数据时，队列会从生产者手中获取数据，并缓存在队列内部，而生产者立即返回。
 * <p>
 * 只有当队列缓冲区达到最大值缓存容量时（LinkedBlockingQueue 可以通过构造函数指定该值），才会阻塞生产者队列，直到消费者从队列中消费掉一份数据，
 * 生产者线程会被唤醒，反之对于消费者这端的处理也基于同样的原理。而 LinkedBlockingQueue 之所以能够高效的处理并发数据，还因为其对于生产者端和
 * 消费者端分别采用了独立的锁来控制数据同步，这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据，以此来提高整个队列的并发性能。
 * <p>
 * 作为开发者，我们需要注意的是，如果构造一个 LinkedBlockingQueue 对象，而没有指定其容量大小，LinkedBlockingQueue 会默认一个类似无限大小的
 * 容量（Integer.MAX_VALUE），这样的话，如果生产者的速度一旦大于消费者的速度，也许还没有等到队列满阻塞产生，系统内存就有可能已被消耗殆尽了。
 * <p>
 * ArrayBlockingQueue 和 LinkedBlockingQueue 是最普通、最常用的阻塞队列，一般情况下，在处理多线程间的生产者消费者问题，使用这两个类足以。
 * <p>
 * LinkedBlockingQueue 不接受 null
 */
public class T04_LinkedBlockingQueue {
    // 因为 2s 是 200m s的 10 倍，所以这里用长度为 10 的队列就够用了
    private static final BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>(10);

    /**
     * 有两个线程 A、B，A 线程每 200ms 就生成一个 [0,100] 之间的随机数，B 线程每 2S 中打印出 A 线程所产生的增量随机数
     */
    public static void main(String[] args) {
        ScheduledExecutorService productExecutor = Executors.newScheduledThreadPool(1);
        ThreadLocalRandom random = ThreadLocalRandom.current();
        productExecutor.scheduleAtFixedRate(() -> {
            try {
                blockingQueue.offer(random.nextInt(101)); // 把生成的随机数放进去
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }, 0, 200, TimeUnit.MILLISECONDS); // 每 200 毫秒执行线程

        // 用一个线程去循环消费，这里用 sleep 去实现
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(2000);
                    System.out.println("=============开始取值=============");
                    List<Integer> list = new LinkedList<>();
                    blockingQueue.drainTo(list);  // drainTo() 将队列中的值全部从队列中移除，并赋值给对应集合
                    list.forEach(System.out::println);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
